package com.ayitubie.gateway;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

@Component
public class DecryptParamsFilter extends AbstractGatewayFilterFactory<DecryptParamsFilter.Config> {

    @Autowired
    CryptoUtil cryptoUtil;
    public DecryptParamsFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String method = request.getMethodValue();

            URI uri = request.getURI();
            if (uri.getPath().equals("/user/login") || uri.getPath().equals("/user/getLoginUserInfo")) {
                ServerHttpRequest modifiedRequest = request;

                if ("GET".equals(method)) {
                    modifiedRequest = decryptGetParams(request);
                } else if ("POST".equals(method)) {
                    return decryptPostParams(exchange, chain);
                } else if ("PUT".equals(method) || "DELETE".equals(method)) {
                    return decryptBodyForOtherMethods(exchange, chain);
                }

                return chain.filter(exchange.mutate().request(modifiedRequest).build());
            }
            return chain.filter(exchange);
        };
    }

    private ServerHttpRequest decryptGetParams(ServerHttpRequest request) {
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        MultiValueMap<String, String> decryptedParams = new LinkedMultiValueMap<>();

        for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
            String key = entry.getKey();
            List<String> values = entry.getValue();

            for (String value : values) {
                String decryptedValue = cryptoUtil.desEncrypt(value);
                decryptedParams.add(key, decryptedValue);
            }
        }

        URI uri = request.getURI();
        URI newUri = URI.create(uri.getScheme() + "://" + uri.getAuthority() + uri.getPath() + "?" + createQueryString(decryptedParams));
        return request.mutate().uri(newUri).build();
    }

    private String createQueryString(MultiValueMap<String, String> params) {
        StringBuilder queryString = new StringBuilder();
        for (Map.Entry<String, List<String>> entry : params.entrySet()) {
            String key = entry.getKey();
            for (String value : entry.getValue()) {
                if (queryString.length() > 0) {
                    queryString.append("&");
                }
                queryString.append(key).append("=").append(value);
            }
        }
        return queryString.toString();
    }

    private Mono<Void> decryptPostParams(ServerWebExchange exchange, GatewayFilterChain chain) {
        return new ModifyRequestBodyGatewayFilterFactory().apply(c -> c.setRewriteFunction(String.class, String.class, (ex, body) -> {
            String decryptedBody = cryptoUtil.desEncrypt(body);
            return Mono.just(decryptedBody);
        })).filter(exchange, chain);
    }

    private Mono<Void> decryptBodyForOtherMethods(ServerWebExchange exchange, GatewayFilterChain chain) {
        return new ModifyRequestBodyGatewayFilterFactory().apply(c -> c.setRewriteFunction(String.class, String.class, (ex, body) -> {
            String decryptedBody = cryptoUtil.desEncrypt(body);
            return Mono.just(decryptedBody);
        })).filter(exchange, chain);
    }

    private String decrypt(String value) {
        // Implement your decryption logic here
        // For example, decrypting a Base64 encoded string
        try {
            byte[] decodedBytes = java.util.Base64.getDecoder().decode(value);
            return new String(decodedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return value; // Return original value if decryption fails
        }
    }

    public static class Config {
        // Configuration properties, if any
    }
}